import * as THREE from "three";
// import { CSG } from "three-csg-ts";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry";
import Stats from "three/examples/jsm/libs/stats.module";
import { Font, FontLoader } from "three/examples/jsm/loaders/FontLoader";
import { SVGLoader } from "three/examples/jsm/loaders/SVGLoader";
import { TTFLoader } from "three/examples/jsm/loaders/TTFLoader";

export default class ThreeJs {
  stats: Stats | null = null;
  scene: THREE.Scene | null = null;
  camera: THREE.PerspectiveCamera | null = null;
  renderer: THREE.WebGLRenderer | null = null;
  ambientLight: THREE.AmbientLight | null = null;
  title: THREE.Mesh | null = null;
  titleGroup: THREE.Group | null = null;
  textGroup: THREE.Group | null = null;
  logoGroup: THREE.Group | null = null;
  starLeftGroup: THREE.Group | null = null;
  starRightGroup: THREE.Group | null = null;
  fourStarLeftGroup: THREE.Group | null = null;
  fourStarRightGroup: THREE.Group | null = null;
  controls: OrbitControls | null = null;
  axesHelper: THREE.AxesHelper | null = null;

  constructor() {
    this.init();
  }

  init(): void {
    this.setLoadingManager();
    // this.setStats();
    this.setRenderer();
    this.setScene();
    this.setCamera();
    this.setLight();
    this.setCube();
    this.setControls();
    this.animate();
    this.onWindowResize();
    // this.setAxesHelper();
  }

  setLoadingManager(): void {
    const manager = new THREE.LoadingManager();
    manager.onStart = function (url, itemsLoaded, itemsTotal) {
      console.log(
        `Started loading file: ${url}.Loaded ${itemsLoaded} of ${itemsTotal} files.`
      );
    };

    manager.onLoad = function () {
      console.log("Loading complete!");
    };

    manager.onProgress = function (url, itemsLoaded, itemsTotal) {
      console.log(
        `Loading file: ${url} .Loaded ${itemsLoaded}  of ${itemsTotal} files.`
      );
    };

    manager.onError = function (url) {
      console.log(`There was an error loading ${url}`);
    };
  }

  onWindowResize(): void {
    window.addEventListener("resize", () => {
      if (this.camera && this.renderer) {
        this.camera.aspect = window.innerWidth / window.innerHeight;
        this.camera.updateProjectionMatrix();
        this.render();
        this.renderer.setSize(window.innerWidth, window.innerHeight);
      }
    });
  }
  //辅助工具
  setAxesHelper(): void {
    const helper = new THREE.AxesHelper(50);
    this.scene && this.scene.add(helper);
  }

  setStats(): void {
    this.stats = Stats();
    document.body.appendChild(this.stats.dom);
  }

  // 第一步新建一个场景
  setScene(): void {
    this.scene = new THREE.Scene();
    this.scene.background = new THREE.Color(0xe55439);
  }

  // 新建透视相机
  setCamera(): void {
    // 第二参数就是 长度和宽度比 默认采用浏览器  返回以像素为单位的窗口的内部宽度和高度
    this.camera = new THREE.PerspectiveCamera(
      45,
      window.innerWidth / window.innerHeight,
      0.1,
      1000
    );
    this.camera.position.set(0, 40, 300);
    this.camera.lookAt(new THREE.Vector3(0, 0, 0));
  }

  // 设置渲染器
  setRenderer(): void {
    this.renderer = new THREE.WebGLRenderer({ antialias: true });
    // 设置画布的大小
    this.renderer.setSize(window.innerWidth, window.innerHeight);
    //告诉渲染器需要阴影效果
    this.renderer.shadowMap.enabled = true;
    this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 默认的是，没有设置的这个清晰 THREE.PCFShadowMap
    //这里 其实就是canvas 画布  renderer.domElement
    document.body.appendChild(this.renderer.domElement);
  }

  // 设置环境光
  setLight(): void {
    if (this.scene) {
      this.scene && this.scene.add(new THREE.AmbientLight(0x444444));
      const light = new THREE.PointLight(0xffffff);
      light.position.set(10, 0, 100);
      //告诉平行光需要开启阴影投射
      light.castShadow = true;
      this.scene && this.scene.add(light);
    }
  }
  /**
   *  MeshBasicMaterial：为几何体赋予一种简单的颜色，或者显示几何体的线框
      MeshDepthMaterial：根据网格到相机的距离，该材质决定如何给网格染色
      MeshNormalMaterial：根据物体表面的法向量计算颜色
      MeshFaceMaterial：这是一种容器，可以在该容器中为物体的各个表面上设置不同的颜色
      MeshLambertMaterial：考虑光照的影响，可以创建颜色暗淡，不光亮的物体
      MeshPhongMaterial：考虑光照的影响，可以创建光亮的物体
      ShaderMaterial：使用自定义的着色器程序，直接控制顶点的放置方式，以及像素的着色方式
      LineBasicMaterial：可以用于THREE.Line几何体，从而创建着色的直线
      LineDashedMaterial：类似与基础材质，但可以创建虚线效果
   */

  // 创建网格模型
  setCube(): void {
    // this.setStar();
    // this.setTitleGroup();
    // this.setTitle();
    // this.setText();
    // this.setLogo();
    this.setFourStar();
  }

  setFourStar(): void {
    this.setFourStarLeft();
    // this.setFourStarRight();
  }

  setFourStarLeft(): void {
    this.fourStarLeftGroup = new THREE.Group();
    const fourStarLeftGroup = this.setFourStarGroup();
    this.fourStarLeftGroup.add(fourStarLeftGroup);
    this.fourStarLeftGroup.position.set(-25, 0, 0);
    this.fourStarLeftGroup.scale.set(0.8, 0.8, 0.8);
    this.scene && this.scene.add(this.fourStarLeftGroup);
  }

  setFourStarRight(): void {
    this.fourStarRightGroup = new THREE.Group();
    const fourStarRightGroup = this.setFourStarGroup();
    this.fourStarRightGroup.add(fourStarRightGroup);
    this.fourStarRightGroup.position.set(25, 20, 0);
    this.scene && this.scene.add(this.fourStarRightGroup);
  }

  setFourStarGroup(): THREE.Group {
    const fourStarGroup = new THREE.Group();
    for (let i = 0; i < 1; i++) {
      const geometry = new THREE.ConeGeometry(3, 6, 4);
      const material = new THREE.MeshLambertMaterial({
        color: 0xffcea3,
        opacity: 0.7,
      });
      const mesh = new THREE.Mesh(geometry, material);
      const cone = this.setPivot(0, -3, 0, mesh);
      cone.rotateZ(Math.PI / 2);
      cone.rotateZ((Math.PI / 2) * i);
      fourStarGroup.add(cone);
    }
    fourStarGroup.translateY(3);
    return fourStarGroup;
  }

  setStar(): void {
    this.setStarLeft();
    this.setStarRight();
  }

  setStarLeft(): void {
    this.starLeftGroup = new THREE.Group();
    const starLeftGroup = this.setStarGroup();
    this.starLeftGroup.add(starLeftGroup);
    this.starLeftGroup.position.set(-25, 60, 0);
    this.scene && this.scene.add(this.starLeftGroup);
  }

  setStarRight(): void {
    this.starRightGroup = new THREE.Group();
    const starRightGroup = this.setStarGroup();
    this.starRightGroup.add(starRightGroup);
    this.starRightGroup.position.set(30, 55, 0);
    this.scene && this.scene.add(this.starRightGroup);
  }

  setStarGroup(): THREE.Group {
    const starLeftGroup = new THREE.Group();
    for (let i = 0; i < 7; i++) {
      const geometry = new THREE.CylinderGeometry(0.2, 0.2, 2.5, 32);
      const material = new THREE.MeshLambertMaterial({ color: 0xffcea3 });
      const cylinder = new THREE.Mesh(geometry, material);
      const mesh = this.setPivot(0, 3, 0, cylinder);
      mesh.rotateZ((Math.PI / 7) * 2 * i);
      starLeftGroup && starLeftGroup.add(mesh);
    }
    starLeftGroup.translateY(-3);
    return starLeftGroup;
  }

  //通过x,y,z指定旋转中心，obj是要旋转的对象
  setPivot(
    x: number,
    y: number,
    z: number,
    obj: THREE.Object3D
  ): THREE.Object3D {
    const wrapper = new THREE.Object3D();
    wrapper.position.set(x, y, z);
    wrapper.add(obj);
    obj.position.set(-x, -y, -z);
    return wrapper;
  }

  setTitle(): void {
    const loader = new TTFLoader();
    loader.load("/assets/fonts/STHeiti.ttf", (res) => {
      if (this.scene) {
        const json = new Font(res);
        const text = "恭喜发财，大吉大利";
        const font = new TextGeometry(text, {
          font: json,
          size: 4, //字体大小
          height: 0.01, // 挤出文本的厚度
          curveSegments: 12, // 曲线上点的数量。默认值为12
          bevelEnabled: true, // 是否开启斜角，默认为false
          bevelThickness: 0.5, // 文本上斜角的深度，默认值为20
          bevelSize: 0.03, //斜角与原始文本轮廓之间的延伸距离。默认值为8
          bevelSegments: 3, // 斜角的分段数。默认值为3
        });
        font.center();
        const material = new THREE.MeshLambertMaterial({
          color: 0xffcea3,
        });
        this.title = new THREE.Mesh(font, material);
        this.title.position.set(0, 45, 0);
        this.scene.add(this.title);
      }
    });
  }

  setText(): void {
    const loader = new FontLoader();
    loader.load("/assets/fonts/helvetiker_bold.typeface.json", (res) => {
      if (this.scene) {
        this.textGroup = new THREE.Group();
        const text = "2022@JUEJIN@HAPPYNEWYEAR2022@JUEJIN@HAPPYNEWYEAR";
        for (let i = 0; i < text.length; i++) {
          const x =
            (Math.floor(Math.cos((Math.PI / 24) * i) * 100) / 100) * -34;
          const y = 0;
          const z = (Math.floor(Math.sin((Math.PI / 24) * i) * 100) / 100) * 34;
          const font = new TextGeometry(text[i], {
            font: res,
            size: 4, //字体大小
            height: 0.01, // 挤出文本的厚度
            curveSegments: 12, // 曲线上点的数量。默认值为12
            bevelEnabled: true, // 是否开启斜角，默认为false
            bevelThickness: 0.5, // 文本上斜角的深度，默认值为20
            bevelSize: 0.03, //斜角与原始文本轮廓之间的延伸距离。默认值为8
            bevelSegments: 3, // 斜角的分段数。默认值为3
          });
          font.center();
          const material = new THREE.MeshLambertMaterial({
            color: 0xffcea3,
          });
          const textMesh = new THREE.Mesh(font, material);
          textMesh.position.set(x, y, z);
          textMesh.rotateY((Math.PI / 24) * i - Math.PI / 2);
          this.textGroup && this.textGroup.add(textMesh);
        }
        this.textGroup.rotateY(Math.PI * -0.2);
        this.textGroup.rotateZ(Math.PI * -0.06);
        this.textGroup.position.set(0, 10, 0);
        this.scene.add(this.textGroup);
      }
    });
  }

  setLogo(): void {
    const cone = this.setCone();
    const cylinder1 = this.setCylinder1();
    const cylinder2 = this.setCylinder2();
    this.logoGroup = new THREE.Group();
    this.logoGroup.add(cone);
    this.logoGroup.add(cylinder1);
    this.logoGroup.add(cylinder2);
    this.logoGroup.rotateY(Math.PI * 0.2);
    this.logoGroup.rotateZ(Math.PI * 0.1);
    this.scene && this.scene.add(this.logoGroup);
  }

  setCone(): THREE.Mesh {
    const geometry = new THREE.ConeGeometry(5, 5, 4, 1, true, 0, 6.3);
    const material = new THREE.MeshLambertMaterial({
      color: new THREE.Color(0xffcea3),
      side: THREE.DoubleSide,
    });
    const cone = new THREE.Mesh(geometry, material);
    cone.position.set(0, 20, 0);
    cone.castShadow = true;
    return cone;
  }

  setCylinder1(): THREE.Mesh {
    const geometry = new THREE.CylinderGeometry(10, 14, 5, 4, 1, true);
    const material = new THREE.MeshLambertMaterial({
      color: new THREE.Color(0xffcea3),
      side: THREE.DoubleSide,
    });
    const cylinder1 = new THREE.Mesh(geometry, material);
    cylinder1.position.set(0, 10, 0);
    cylinder1.castShadow = true;

    return cylinder1;
  }

  setCylinder2(): THREE.Mesh {
    const geometry = new THREE.CylinderGeometry(20, 24, 5, 4, 1, true);
    const material = new THREE.MeshLambertMaterial({
      color: new THREE.Color(0xffcea3),
      side: THREE.DoubleSide,
    });
    const cylinder2 = new THREE.Mesh(geometry, material);
    cylinder2.position.set(0, 0, 0);
    cylinder2.castShadow = true;
    return cylinder2;
  }

  setTitleGroup(): void {
    const loader = new SVGLoader();
    loader.load(
      "/assets/imgs/title.svg",
      (data) => {
        const paths = data.paths;
        this.titleGroup = new THREE.Group();
        for (let i = 0; i < paths.length; i++) {
          const path = paths[i];

          const material = new THREE.MeshLambertMaterial({
            color: 0xffcea3,
            side: THREE.DoubleSide,
            depthWrite: false,
          });

          const shapes = SVGLoader.createShapes(path);

          for (let j = 0; j < shapes.length; j++) {
            const shape = shapes[j];
            const geometry = new THREE.ShapeGeometry(shape);

            const mesh = new THREE.Mesh(geometry, material);
            this.titleGroup && this.titleGroup.add(mesh);
          }
        }
        this.titleGroup.scale.set(0.2, 0.2, 0.2);
        this.titleGroup.rotateX(Math.PI);
        this.setCenter(this.titleGroup);
        this.titleGroup.position.y = 70;
        this.scene && this.titleGroup && this.scene.add(this.titleGroup);
      },
      // called when loading is in progresses
      function (xhr) {
        console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
      },
      // called when loading has errors
      function (error) {
        console.log("An error happened", error);
      }
    );
  }

  setCenter(group: THREE.Group): void {
    const box = new THREE.Box3();
    //通过传入的object3D对象来返回当前模型的最小大小，值可以使一个mesh也可以使group
    box.expandByObject(group);

    const mdlen = box.max.x - box.min.x;
    const mdwid = box.max.z - box.min.z;
    const mdhei = box.max.y - box.min.y;
    // const centerpoint = new THREE.Vector3();
    const x1 = box.min.x + mdlen / 2;
    const y1 = box.min.y + mdhei / 2;
    const z1 = box.min.z + mdwid / 2;

    group.position.set(-x1, -y1, -z1);
  }

  // 渲染
  render(): void {
    if (this.renderer && this.scene && this.camera) {
      this.renderer.render(this.scene, this.camera);
    }
  }

  // 动画
  animate(): void {
    this.render();
    this.controls && this.controls.update();
    this.stats && this.stats.update();
    requestAnimationFrame(this.animate.bind(this));
    if (this.logoGroup) {
      const axis = new THREE.Vector3(0, 1, 0); //向量axis
      this.logoGroup.rotateOnAxis(axis, Math.PI / 200); //绕axis轴旋转π/8
    }

    if (this.textGroup) {
      const axis = new THREE.Vector3(0, 1, 0); //向量axis
      this.textGroup.rotateOnAxis(axis, -Math.PI / 200); //绕axis轴旋转π/8
    }

    if (this.starLeftGroup) {
      const axis = new THREE.Vector3(0, 0, 1); //向量axis
      this.starLeftGroup.rotateOnAxis(axis, -Math.PI / 200); //绕axis轴旋转π/8
    }

    if (this.starRightGroup) {
      const axis = new THREE.Vector3(0, 0, 1); //向量axis
      this.starRightGroup.rotateOnAxis(axis, -Math.PI / 200); //绕axis轴旋转π/8
    }

    // if (this.fourStarRightGroup) {
    //   const axis = new THREE.Vector3(0, 1, 0); //向量axis
    //   this.fourStarRightGroup.rotateOnAxis(axis, -Math.PI / 200); //绕axis轴旋转π/8
    // }

    // if (this.fourStarLeftGroup) {
    //   const axis = new THREE.Vector3(0, 1, 0); //向量axis
    //   this.fourStarLeftGroup.rotateOnAxis(axis, -Math.PI / 200); //绕axis轴旋转π/8
    // }
  }

  setControls(): void {
    if (this.camera && this.renderer) {
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);

      // 如果使用animate方法时，将此函数删除
      //controls.addEventListener( 'change', render );
      // 使动画循环使用时阻尼或自转 意思是否有惯性
      this.controls.enableDamping = true;
      //动态阻尼系数 就是鼠标拖拽旋转灵敏度
      this.controls.dampingFactor = 0.25;
      //是否可以缩放
      this.controls.enableZoom = true;
      //是否自动旋转
      this.controls.autoRotate = false;
      //设置相机距离原点的最远距离
      this.controls.minDistance = 1;
      //设置相机距离原点的最远距离
      this.controls.maxDistance = 200;
      //是否开启右键拖拽
      this.controls.enablePan = true;
    }
  }
}
